import org.apache.hadoop.hive.ql.exec.UDF;

import java.util.HashSet;

public class IdCardUDF extends UDF {
    // 常量内部管理类
    private static final class CONSTAINS {
        // 其他省份编码值
        private static final HashSet<Integer> provincesCode = new HashSet<Integer>();
        // 成功标识符
        private static final String SUCCESS = "true";
        // 失败标识符
        private static final String FAILED = "false";
        // 身份证格式校验正则
        private static final String REG_IDNUMBER = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3})";
        // 护照
        private static final String REGEX_ID_HKMT = "([A-Z]{1,2}[0-9]{6}[0-9A]{0,1})|([1|5|7|S|s|M|m][0-9]{6,8}\\([0-9Aa]\\){0,1})|([A-Z][0-9]{9})|([0-9]{8})|(8300.*)";
        // 通行证
        private static final String REGEX_ID_PASSPORT = "^1[45][0-9]{7}|([P|p|S|s][0-9]{7})|([S|s|G|g|K][0-9]{8})|([Gg|Tt|Ss|Ll|Qq|Dd|Aa|Ff][0-9]{8})|([H|h|M|m|E|5]\\d{8,10})$";
        // 军官证
        private static final String REGEX_ID_COO = "[一-龥]字第[0-9]{4,12}号{0,1}";

        static {
        /*
            这里初始化的地区编码仅仅是省份编码，
            是因为经过核对GB/T2260-1999中的完整地区编码很多与身份证号的地区编码不符，
            因此没有比对完整的地区编码
        */
            provincesCode.add(11);
            provincesCode.add(12);
            provincesCode.add(13);
            provincesCode.add(14);
            provincesCode.add(15);
            provincesCode.add(21);
            provincesCode.add(22);
            provincesCode.add(23);
            provincesCode.add(31);
            provincesCode.add(32);
            provincesCode.add(33);
            provincesCode.add(34);
            provincesCode.add(35);
            provincesCode.add(36);
            provincesCode.add(37);
            provincesCode.add(41);
            provincesCode.add(42);
            provincesCode.add(43);
            provincesCode.add(44);
            provincesCode.add(45);
            provincesCode.add(46);
            provincesCode.add(50);
            provincesCode.add(51);
            provincesCode.add(52);
            provincesCode.add(53);
            provincesCode.add(54);
            provincesCode.add(61);
            provincesCode.add(62);
            provincesCode.add(63);
            provincesCode.add(64);
            provincesCode.add(65);
            provincesCode.add(71); // 台湾
            provincesCode.add(81); // 香港
            provincesCode.add(82); // 澳门
        }
    }

    /**
     * @param sfzjlx 身份证件类型
     * @param sfzjhm 身份证号
     * @return 返回成功失败对应编码值（此处fasle为失败，true为成功）
     */
    public static String evaluate(String sfzjlx, String sfzjhm) {
        if (null == sfzjhm || "".equals(sfzjhm)) {
            return CONSTAINS.FAILED;
        } else {
            switch (sfzjlx) {
                case "1": // 居民身份证
                    // 去除空格并大写(后续校验位无需考虑大小写)
                    sfzjhm = sfzjhm.trim().toUpperCase();
                    if (!sfzjhm.matches(CONSTAINS.REG_IDNUMBER)) { // 不满足身份证基本正则为失败
                        return CONSTAINS.FAILED;
                    }
                    // 校验身份证地区码
                    int prov = Integer.parseInt(sfzjhm.substring(0, 2));
                    if (!CONSTAINS.provincesCode.contains(prov)) {
                        return CONSTAINS.FAILED;
                    }
                    // 最后一位校验位校验
                    if (!sfzjhm.substring(sfzjhm.length() - 1).equals(getVerifyCode(sfzjhm.substring(0, sfzjhm.length() - 1)))) {
                        return CONSTAINS.FAILED;
                    }
                    return CONSTAINS.SUCCESS;
                case "6": // 香港特区护照/身份证明
                case "7": // 澳门特区护照/身份证明
                case "8": // 台湾居民来往大陆通行证
                case "9": // 境外永久居住证
                case "Z": // 其他（判断规则类同）
                case "A": // 护照
                    if (sfzjhm.matches(CONSTAINS.REG_IDNUMBER)) { // 身份证格式，返回true
                        return CONSTAINS.SUCCESS;
                    }
                    if (sfzjhm.matches(CONSTAINS.REGEX_ID_HKMT) || sfzjhm.matches(CONSTAINS.REGEX_ID_PASSPORT)) { //护照或通行证，返回true
                        return CONSTAINS.SUCCESS;
                    }
                    return CONSTAINS.FAILED;
                case "2": // 士官证
                case "3": // 军官证
                    if (sfzjhm.matches(CONSTAINS.REGEX_ID_COO)) {
                        return CONSTAINS.SUCCESS;
                    }
                    // 可能也是护照身份证或者通行证
                    if (sfzjhm.matches(CONSTAINS.REG_IDNUMBER) || sfzjhm.matches(CONSTAINS.REGEX_ID_HKMT) || sfzjhm.matches(CONSTAINS.REGEX_ID_PASSPORT)) {
                        return CONSTAINS.SUCCESS;
                    }
                    return CONSTAINS.FAILED;
                case "4": // 文职干部证
                case "5": // 部队离退休证
                    if ("无".equals(sfzjhm) || "没有".equals(sfzjhm)) {
                        return CONSTAINS.FAILED;
                    }
                    // 格式较乱且暂未找到统一编号标准，暂不做严格校验
                    return CONSTAINS.SUCCESS;
                case "B": // 户口簿
                case "0": // 无
                    if (sfzjhm.matches(CONSTAINS.REG_IDNUMBER)) { // 仅校验身份证是否满足
                        return CONSTAINS.SUCCESS;
                    }
                    return CONSTAINS.FAILED;
                default: // 默认情况下，判断是否满足已知证件类型规则
                    if (sfzjhm.matches(CONSTAINS.REG_IDNUMBER) || sfzjhm.matches(CONSTAINS.REGEX_ID_COO) ||
                            sfzjhm.matches(CONSTAINS.REGEX_ID_HKMT) || sfzjhm.matches(CONSTAINS.REGEX_ID_PASSPORT)) {
                        return CONSTAINS.SUCCESS;
                    } else {
                        return CONSTAINS.FAILED;
                    }
            }
        }
    }


    /**
     * 获取校验位
     *
     * @param idNumber_18 18位身份证
     * @return 18位身份证 + 校验位
     */
    private static String getVerifyCode(String idNumber_18) {
        String[] ValCodeArr = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
        String[] Wi = {"7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", "9", "10", "5", "8", "4", "2"};
        // 加权校验和
        int power = 0;
        for (int i = 0; i < 17; i++) {
            power = power + Integer.parseInt(String.valueOf(idNumber_18.charAt(i))) * Integer.parseInt(Wi[i]);
        }
        return ValCodeArr[power % 11];
    }
}
